#include "HuffmanArchiver.h"
#include "FrequencyTable.h"
#include "HTree.h"
#include "HuffmanOptions.h"
#include "DataSource.h"
#include "DataTarget.h"
#include "IFTBuilder.h"
#include "FrequencyTableCreator.h"
#include "FrequencyTableRestorer.h"
#include "IHTreeBuilder.h"
#include "TreeBuilder.h"
#include <assert.h>
#ifdef _DEBUG
#include <iostream>
#endif

HuffmanArchiver::~HuffmanArchiver(void)
{
	if (table)
		delete table;
}

void HuffmanArchiver::archive(DataSource* s, DataTarget* t)
{
	IFTBuilder* ftBuilder = new FrequencyTableCreator();
	IHTreeBuilder* treeBuilder = new TreeBuilder();
	process(s,t, ftBuilder, treeBuilder, ArchivingMode::Archive);
	delete ftBuilder;
	delete treeBuilder;	
}

void HuffmanArchiver::unarchive(DataSource* s, DataTarget* t)
{
	IFTBuilder* ftBuilder = new FrequencyTableRestorer();
	IHTreeBuilder* treeBuilder = new TreeBuilder();
	process(s,t, ftBuilder, treeBuilder, ArchivingMode::UnArchive);
	delete ftBuilder;
	delete treeBuilder;	
}

void HuffmanArchiver::acceptFinishedByte(uint8_t byte)
{
	target->putNextByte(byte);
}

void HuffmanArchiver::process(DataSource* s, DataTarget*t, IFTBuilder* ftBuilder, IHTreeBuilder* treeBuilder, ArchivingMode mode)
{
#ifdef _DEBUG
	std::cout << "Start " << ((ArchivingMode::Archive == mode) ? "archiving." : "unarchiving.") << std::endl;
#endif
	this->source = s;
	this->target = t;
	table = ftBuilder->buildTable(s);
	if (ArchivingMode::Archive == mode)
	{
		writeFrequencyTable(table, t);
	}

	tree  = treeBuilder->buildTree(table);
	while (s->hasNextByte())
	{
		uint8_t nextByte = s->getNextByte();
		//switch (mode)
		//{
		//case Archive:
		//	//
		//	break;
		//case UnArchive:
		//	tree->unArchivebyte(nextByte);
		//	break;
		//default:
		//	assert(0);
		//}
	}
#ifdef _DEBUG
	std::cout << "Finished " << ((ArchivingMode::Archive == mode) ? "archiving." : "unarchiving.") << std::endl;
#endif
}

void HuffmanArchiver::writeFrequencyTable(FrequencyTable* table, DataTarget* target)
{
	size_t n;
	uint8_t* repr = table->byteRepresentation(n);
	for (size_t i = 0; i < n; ++i)
	{
		target->putNextByte(repr[i]);
	}
	delete[] repr;
}
